home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / win / c / fixds.com / FIXDS.DOC < prev    next >
Encoding:
Text File  |  1989-04-18  |  10.9 KB  |  242 lines

  1. -----------------------------------------------------------------------------
  2. FIXDS Version 2.0 by Michael Geary                                    4/18/89
  3. -----------------------------------------------------------------------------
  4. Changes from version 1.0:
  5. Now processes the .EXE file instead of .OBJ for better reliability.
  6. -----------------------------------------------------------------------------
  7. Summary:
  8. Eliminates need for EXPORTS and MakeProcInstance() in Windows applications.
  9. Prevents bugs caused by omitting EXPORTS or MakeProcInstance().
  10. Allows Windows applications to export functions that will be called
  11. directly from a dynamic link library.
  12. -----------------------------------------------------------------------------
  13.  
  14. Have you ever forgotten to EXPORT a window function?  How about good
  15. old MakeProcInstance() - ever had a weirdo crash because you forgot
  16. this one?  Worse yet, how about the time you tried to directly call a
  17. function you had EXPORTed?  If you're like me, these have probably
  18. happened more times than you'd like to admit.
  19.  
  20. With FIXDS, you will never have to use EXPORTS or MakeProcInstance()
  21. in a Windows application again!  All you have to do is run FIXDS on
  22. your .EXE file.  For example, in your MAKE file:
  23.  
  24. foo.exe:    foo.obj ...
  25.     link @foo.lnk
  26.     fixds foo.exe
  27.     ...
  28.  
  29. FIXDS can be run before or after RC; it doesn't matter.
  30.  
  31. Now you can get rid of the EXPORTS and MakeProcInstance() nonsense
  32. you've been dealing with. You don't have to EXPORT window functions
  33. or any other callback functions.  You don't have to do a
  34. MakeProcInstance() on any function, ever.  And, it's perfectly safe
  35. to directly call any callback functions you may have.
  36.  
  37. You can take out the entire EXPORTS section from your application's
  38. .DEF file.  You can take out every MakeProcInstance() and
  39. FreeProcInstance() call.  The places you used the return value from
  40. MakeProcInstance() (such as a DialogBox() call), you can just use the
  41. actual function name instead.
  42.  
  43. Your code will even work if you leave in some MakeProcInstance's or
  44. EXPORTs.  Once you start using FIXDS, they just become irrelevant -
  45. your code will work with or without them.
  46.  
  47. FIXDS also gives you one useful capability that isn't ordinarily
  48. available.  Your Windows application can export functions which will
  49. be directly called from a dynamic link library.  This can be
  50. extremely handy. Some Windows applications can be extended by loading
  51. and calling DLL's at runtime.  (SQLWindows and Excel are examples of
  52. this.) Normally, these DLL's can not make any calls to functions in
  53. the application.  That's a bummer, since you probably have a slew of
  54. utility functions in the application that a custom DLL might put to
  55. good use.  With normal Windows code, you can't just make normal C
  56. calls to these functions.  You *could* do MakeProcInstance() on all
  57. of them and make a table of the addresses, and then pass this table
  58. to the DLL so it could make indirect calls to the application
  59. functions, but with FIXDS there is an easier way.  Just list in the
  60. EXPORTS section of your application's .DEF file the functions you
  61. wish to make available to the DLL's.  (This is the only case where
  62. you need an EXPORTS section, and only the functions you want
  63. available to DLL's should be listed.)  Then run IMPLIB on this .DEF
  64. file and presto - you have a .LIB that you can link with your DLL's. 
  65. The DLL's can then make direct calls to these functions, and the
  66. references will be fixed up at runtime just as if the called
  67. functions were in some other DLL.
  68.  
  69.  
  70. Now, you've probably got three questions:
  71.  
  72. "What's the catch?"
  73.  
  74. "How does this thing work, anyway?"
  75.  
  76. "If it does work, why in the world has Microsoft been putting us
  77. through all the EXPORTS and MakeProcInstance() grief all this time?"
  78.  
  79. Well, I can answer the first two questions.  Darned if I know the
  80. answer to the third, though!
  81.  
  82. First, what's the catch?  Not much.  FIXDS will work with any
  83. Windows application that has its own stack.  (I read once that in
  84. theory it's possible to build an application that doesn't have its
  85. own stack, but I don't know if anyone has ever tried that.  If your
  86. application doesn't have its own stack, FIXDS won't work with it.) 
  87. FIXDS is for applications only, not dynamic link libraries.  It
  88. depends on the fact that SS == DS, which is true only in application
  89. code.
  90.  
  91. There's no other catch I can think of.  FIXDS is completely compatible
  92. with every version of Windows, and with every compiler that generates
  93. the standard Windows function prolog.
  94.  
  95. Now for the second question, how does this pup work?  I'll give a
  96. brief explanation here; for more information on the same topic, see
  97. Chapter 8 of Charles Petzold's _Programming Windows_.
  98.  
  99. The basic problem that EXPORTS and MakeProcInstance() try to solve is
  100. getting the proper value into the DS register.  Each FAR function in
  101. a Windows application begins with this prolog:
  102.  
  103.         push    ds          ; May have "mov ax,ds" instead of these
  104.         pop     ax          ; two instructions.  Does the same thing.
  105.         nop
  106.         inc     bp
  107.         push    bp
  108.         mov     bp, sp
  109.         push    ds
  110.         mov     ds, ax
  111.  
  112. Now, aside for fooling around with BP, the net effect of this code is
  113. to put the same value into DS that it already had (and to save a copy
  114. of DS on the stack, which gets popped back off later).  For a normal
  115. FAR function that you call from your own code, that's a lot of
  116. busywork, but doesn't hurt anything.  After all, you already had the
  117. right DS value since it was your application that called this
  118. function.
  119.  
  120. The busywork starts to come into play when you deal with functions
  121. that are called from outside your application, such as window
  122. functions and other callback functions.  When Windows calls these
  123. functions, there is a different value in DS, so something has to
  124. change.  That's what the EXPORTS and MakeProcInstance() do for you. 
  125. Let's take the case of a dialog function.  The dialog manager inside
  126. Windows (in USER.EXE) simply makes a direct FAR call to the address
  127. you give as your dialog function, and DS at that time points to
  128. USER's data segment, so how does DS get the right value for your
  129. application?
  130.  
  131. Well, when you EXPORT your dialog function, Windows NOP's out the
  132. first two bytes of the prolog, so it looks like this:
  133.  
  134.         nop
  135.         nop
  136.         nop
  137.         inc     bp
  138.         push    bp
  139.         mov     bp, sp
  140.         push    ds
  141.         mov     ds, ax
  142.  
  143. Now you can see that this prolog will end up moving the value from
  144. the AX register into DS (after saving the old DS value).  That's why
  145. you can't just call this function directly from C code - it would
  146. take whatever happened to be laying around in AX and put it into DS;
  147. not a good idea.  However, when you call MakeProcInstance(), Windows
  148. creates an "instance thunk", which looks like this:
  149.  
  150.         mov     ax, XXXX
  151.         jmp     <TheFunction>
  152.  
  153. That XXXX is the key - Windows keeps track of all instance thunks,
  154. and whenever your data segment moves in memory, it patches the XXXX
  155. to reflect the new address of your data segment.  So, that's why you
  156. have to call MakeProcInstance() and then pass that address - the
  157. address of the instance thunk - into CreateDialog() or DialogBox(). 
  158. The dialog manager will then call your instance thunk, which puts the
  159. right value into AX, which the modified function prolog will then
  160. copy over to DS.
  161.  
  162. (I'm leaving out all the details of "reload thunks" - they aren't
  163. relevant to this discussion.)
  164.  
  165. For a window function, essentially the same thing goes on, except
  166. that you only have to do the EXPORTS and not the MakeProcInstance(). 
  167. That's because window functions are only called via SendMessage() or
  168. DispatchMessage(), and those functions essentially do the equivalent
  169. of the instance thunk internally - they put the proper value into AX
  170. before calling your window function.
  171.  
  172. Well, that's all a lot of work just to get the right value into the
  173. DS register, but after all, Windows has to deal with the fact that
  174. your data segment may move around.  Worse yet, it has to handle the
  175. fact that there may be multiple instances of your application.  If it
  176. weren't for that, Windows could just patch your function prolog to
  177. put in the right DS value directly, like it does for dynamic link
  178. libraries.  But, the function code is shared among all instances, and
  179. they all have different data segment addresses.  That's why the
  180. MakeProcInstance() is necessary, so each instance gets its own unique
  181. little header that Windows can patch.
  182.  
  183. So, all that work is necessary, right?  Wrong.  Despite all the work
  184. that MakeProcInstance() and EXPORTS go through to put the correct
  185. value into the DS register, THAT VALUE WAS JUST SITTING IN ANOTHER
  186. REGISTER WAITING TO BE USED.  Which register?  SS.
  187.  
  188. Remember that in a Windows application, SS == DS.  Let me repeat
  189. that, SS == DS.  Now, does any of the function prolog code or the
  190. instance thunk code do anything to SS?  Nope.  Whenever any of your
  191. application code is running, and whenever Windows calls one of your
  192. window functions or callback functions, SS contains your data segment
  193. address.  The prolog code and instance thunks don't have anything to
  194. do with this; Windows' task manager puts the right value into SS
  195. before it lets your task run.  If it didn't, the SS == DS assumption
  196. would be violated.
  197.  
  198. You can probably guess by now what FIXDS does.  It patches all FAR
  199. function prologs to look like this:
  200.  
  201.         mov     ax, ss
  202.         nop
  203.         inc     bp
  204.         push    bp
  205.         mov     bp, sp
  206.         push    ds
  207.         mov     ds, ax
  208.  
  209. Now this prolog works for *all* FAR functions.  Since SS, by
  210. definition, always has the correct data segment value, this prolog
  211. will put the correct value into DS.  It doesn't matter whether it's a
  212. function you call directly or whether it is called back from Windows.
  213.  
  214. This also explains why FIXDS allows you to call application functions
  215. directly from a DLL.  Without FIXDS, you need the EXPORTS and
  216. MakeProcInstance() to get the proper value into DS, and the result of
  217. the MakeProcInstance() call isn't known till runtime, so the best you
  218. can do is an indirect call through the instance thunk.  With FIXDS,
  219. however, the actual function entry point is perfectly usable
  220. regardless of where it is called from.
  221.  
  222.  
  223. The folks at Microsoft didn't believe me when I told them about the
  224. technique that FIXDS uses.  After studying it a bit, they realized
  225. that of course it works.  If it didn't, every application that's been
  226. compiled with the SS == DS assumption would have failed.  Perhaps
  227. there's hope that a future version of Windows or the C compiler will
  228. have something like this built in.  In the meantime, use FIXDS in
  229. your Windows applications and you'll never have to worry about
  230. EXPORTS and MakeProcInstance() again.
  231.  
  232.  
  233. Michael Geary
  234. P.O. Box 1479
  235. Los Gatos, CA  95031
  236.  
  237. BIX:        GEARY
  238. CompuServe: 76704,35
  239. GEnie:      GEARY
  240.  
  241. -----------------------------------------------------------------------------
  242.